<?php

class CArray {

    public static function callback_string($str) {
        if(preg_match('/([^\[]*+)\[(.+)\]/', (string)$str, $match)) {
            $command = $match[1];
            $params = preg_split('/(?<!\\\\),/', $match[2]);
            $params = str_replace('\,', ',', $params);
        } else {
            $command = $str;
            $params = NULL;
        }
        return array($command, $params);
    }

    public static function rotate($source_array, $keep_keys = TRUE) {
        $new_array = array();
        foreach($source_array as $key => $value) {
            $value = ($keep_keys === TRUE) ? $value : array_values($value);
            foreach($value as $k => $v) {
                $new_array[$k][$key] = $v;
            }
        }
        return $new_array;
    }

    public static function remove($key, & $array) {
        if(!array_key_exists($key, $array)) return NULL;
        $val = $array[$key];
        unset($array[$key]);
        return $val;
    }

    public static function extract(array $search, $keys) {
        $keys = array_slice(func_get_args(), 1);
        $found = array();
        foreach($keys as $key) {
            if(isset($search[$key])) {
                $found[$key] = $search[$key];
            } else {
                $found[$key] = NULL;
            }
        }
        return $found;
    }

    public static function unshift_assoc(array & $array, $key, $val) {
        $array = array_reverse($array, TRUE);
        $array[$key] = $val;
        $array = array_reverse($array, TRUE);
        return $array;
    }

    public static function map_recursive($callback, array $array) {
        foreach($array as $key => $val) {
            $array[$key] = is_array($val) ? arr::map_recursive($callback, $val) : call_user_func($callback, $val);
        }
        return $array;
    }

    public static function binary_search($needle, $haystack, $nearest = FALSE, $sort = FALSE) {
        if($sort === TRUE) {
            sort($haystack);
        }
        $high = count($haystack);
        $low = 0;
        while($high - $low > 1) {
            $probe = ($high + $low) / 2;
            if($haystack[$probe] < $needle) {
                $low = $probe;
            } else {
                $high = $probe;
            }
        }
        if($high == count($haystack) OR $haystack[$high] != $needle) {
            if($nearest === FALSE) return FALSE;
            $high_distance = $haystack[ceil($low)] - $needle;
            $low_distance = $needle - $haystack[floor($low)];
            return ($high_distance >= $low_distance) ? $haystack[ceil($low)] : $haystack[floor($low)];
        }
        return $high;
    }

    public static function merge() {
        $total = func_num_args();
        $result = array();
        for($i = 0; $i < $total; $i++) {
            foreach(func_get_arg($i) as $key => $val) {
                if(isset($result[$key])) {
                    if(is_array($val)) {
                        $result[$key] = arr::merge($result[$key], $val);
                    } elseif(is_int($key)) {
                        array_push($result, $val);
                    } else {
                        $result[$key] = $val;
                    }
                } else {
                    $result[$key] = $val;
                }
            }
        }
        return $result;
    }

    public static function overwrite($array1) {
        foreach(array_slice(func_get_args(), 1) as $array2) {
            foreach($array2 as $key => $value) {
                if(array_key_exists($key, $array1)) {
                    $array1[$key] = $value;
                }
            }
        }
        return $array1;
    }

    public static function range($step = 10, $max = 100) {
        if($step < 1) return array();
        $array = array();
        for($i = $step; $i <= $max; $i += $step) {
            $array[$i] = $i;
        }
        return $array;
    }

    public static function to_object(array $array, $class = 'stdClass') {
        $object = new $class;
        foreach($array as $key => $value) {
            if(is_array($value)) {
                $value = arr::to_object($value, $class);
            }
            $object->{$key} = $value;
        }
        return $object;
    }

}
